package com.jfinal.server.undertow;

import com.jfinal.server.undertow.hotswap.ClassLoaderKit;
import com.jfinal.server.undertow.hotswap.HotSwapResolver;
import com.jfinal.server.undertow.ssl.SslConfig;
import io.undertow.Undertow;
import io.undertow.server.handlers.resource.ResourceManager;
import io.undertow.util.StatusCodes;

import java.net.URL;
import java.util.*;
import java.util.zip.Deflater;

/**
 * UndertowConfig
 */
public class UndertowConfig {
    public static final String FILE_NAME = "undertow";
    public static final String FILE_TYPE = ".properties";

    protected String jfinalConfig;

    // 开发模式才支持热加载，此配置与 jfinal 中的是不同的用途
    private boolean devMode = false;
    private int port = 0;
    private String host = "0.0.0.0";
    private String contextPath = "/";
    // web 资源路径
    private final Collection<String> resourcePath = new LinkedHashSet<>(Arrays.asList("src/main/webapp", "WebRoot", "WebContent"));

    private Integer ioThreads = null;
    private Integer workerThreads = null;

    private boolean gzipEnable = false;
    private int gzipLevel = Deflater.DEFAULT_COMPRESSION;
    private int gzipMinLength = 1024;

    private Boolean http2Enable = null;

    private Integer sessionTimeout = null;
    private boolean sessionHotSwap = true;

    private final Collection<String> hotSwapClassPrefix = new LinkedHashSet<>();
    private final Collection<String> systemClassPrefix = new LinkedHashSet<>();

    private SslConfig sslConfig = null;
    private boolean httpToHttps = false;
    private int httpToHttpsStatusCode = StatusCodes.FOUND;
    private boolean httpDisable = false;

    private String serverName = null;

    private Integer bufferSize = null;
    private Boolean directBuffers = null;

    private final Collection<URL> customClassPath = new LinkedList<>();
    private final Collection<String> classPathDirs = new HashSet<>();                // 存放 .class 文件的目录
    protected HotSwapResolver hotSwapResolver;
    protected ClassLoaderKit classLoaderKit;


    public UndertowConfig() {
        configDefaultThreadCount();
        configDefaultServerName();
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    protected void configDefaultServerName() {
        setServerName("JFinal " + UndertowKit.getJFinalVersion());
    }

	protected void configDefaultThreadCount() {
        final int cpuNum = Runtime.getRuntime().availableProcessors();
        final int ioThreadCount = new Double(Math.ceil(cpuNum * 1.6180339)).intValue();
        final int workerThreadCount = ioThreadCount * 16;

        setIoThreads(ioThreadCount);
        setWorkerThreads(workerThreadCount);
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    public ResourceManager getResourceManager() {
        final ResourceManagerBuilder builder = new ResourceManagerBuilder();

        resourcePath.forEach(builder::addResourcePath);

        return builder.build(getClassLoader());
    }

    public ClassLoader getClassLoader() {
        return getClassLoaderKit().getClassLoader();
    }

    public void replaceClassLoader() {
        if (devMode) {
            getClassLoaderKit().replaceClassLoader();
        }
    }

    protected ClassLoaderKit getClassLoaderKit() {
        if (classLoaderKit == null) {
            classLoaderKit = new ClassLoaderKit(customClassPath, Undertow.class.getClassLoader(), getHotSwapResolver(), devMode);
        }
        return classLoaderKit;
    }



    public HotSwapResolver getHotSwapResolver() {
        if (hotSwapResolver == null) {
            setHotSwapResolver(new HotSwapResolver(classPathDirs.toArray(new String[0])));
        }
        return hotSwapResolver;
    }

    public void setHotSwapResolver(HotSwapResolver hotSwapResolver) {
        setHotSwapResolver(hotSwapResolver, true);
    }

    public void setHotSwapResolver(HotSwapResolver hotSwapResolver, boolean applyConfig) {
        if (applyConfig) {
            hotSwapClassPrefix.forEach(hotSwapResolver::addHotSwapClassPrefix);
            systemClassPrefix.forEach(hotSwapResolver::addSystemClassPrefix);
        }

        this.hotSwapResolver = hotSwapResolver;
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    public String getJFinalConfig() {
        return jfinalConfig;
    }

    public void setJFinalConfig(String jfinalConfig) {
        this.jfinalConfig = jfinalConfig;
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    public boolean isDevMode() {
        return devMode;
    }

    public void setDevMode(boolean devMode) {
        this.devMode = devMode;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getContextPath() {
        return contextPath;
    }

    public void setContextPath(String contextPath) {
        this.contextPath = processContextPath(contextPath);
    }

    public Collection<String> getResourcePath() {
        return resourcePath;
    }

	public void addResourcePath(String resourcePath) {
		this.resourcePath.add(resourcePath);
	}

    public Integer getIoThreads() {
        return ioThreads;
    }

    public void setIoThreads(Integer ioThreads) {
        this.ioThreads = ioThreads;
    }

    public Integer getWorkerThreads() {
        return workerThreads;
    }

    public void setWorkerThreads(Integer workerThreads) {
        this.workerThreads = workerThreads;
    }

    public boolean isGzipEnable() {
        return gzipEnable;
    }

    public void setGzipEnable(boolean gzipEnable) {
        this.gzipEnable = gzipEnable;
    }

    public int getGzipLevel() {
        return gzipLevel;
    }

    public void setGzipLevel(int gzipLevel) {
        checkGzipLevel(gzipLevel);

        this.gzipLevel = gzipLevel;
    }

    public int getGzipMinLength() {
        return gzipMinLength;
    }

    public void setGzipMinLength(int gzipMinLength) {
        this.gzipMinLength = gzipMinLength;
    }

    public Boolean getHttp2Enable() {
        return http2Enable;
    }

    public void setHttp2Enable(Boolean http2Enable) {
        this.http2Enable = http2Enable;
    }

    public Integer getSessionTimeout() {
        return sessionTimeout;
    }

    public void setSessionTimeout(Integer sessionTimeout) {
        this.sessionTimeout = sessionTimeout;
    }

    public boolean isSessionHotSwap() {
        return sessionHotSwap;
    }

    public void setSessionHotSwap(boolean sessionHotSwap) {
        this.sessionHotSwap = sessionHotSwap;
    }

    public Collection<String> getHotSwapClassPrefix() {
        return hotSwapClassPrefix;
    }

    public void addHotSwapClassPrefix(String hotSwapClassPrefix) {
        this.hotSwapClassPrefix.add(hotSwapClassPrefix);
    }

    public Collection<String> getSystemClassPrefix() {
        return systemClassPrefix;
    }

    public void addSystemClassPrefix(String systemClassPrefix) {
        this.systemClassPrefix.add(systemClassPrefix);
    }

    public SslConfig getSslConfig() {
        return sslConfig;
    }

    public void setSslConfig(SslConfig sslConfig) {
        this.sslConfig = sslConfig;
    }

    public boolean isHttpToHttps() {
        return httpToHttps;
    }

    public void setHttpToHttps(boolean httpToHttps) {
        this.httpToHttps = httpToHttps;
    }

    public int getHttpToHttpsStatusCode() {
        return httpToHttpsStatusCode;
    }

    public void setHttpToHttpsStatusCode(int httpToHttpsStatusCode) {
        this.httpToHttpsStatusCode = httpToHttpsStatusCode;
    }

    public boolean isHttpDisable() {
        return httpDisable;
    }

    public void setHttpDisable(boolean httpDisable) {
        this.httpDisable = httpDisable;
    }

    public String getServerName() {
        return serverName;
    }

    public void setServerName(String serverName) {
        if ("disabled".equalsIgnoreCase(serverName)) {
            this.serverName = null;
        } else {
            this.serverName = serverName;
        }
    }

    public Integer getBufferSize() {
        return bufferSize;
    }

    public void setBufferSize(Integer bufferSize) {
        this.bufferSize = bufferSize;
    }

    public Boolean getDirectBuffers() {
        return directBuffers;
    }

    public void setDirectBuffers(Boolean directBuffers) {
        this.directBuffers = directBuffers;
    }

    public Collection<URL> getCustomClassPath() {
        return customClassPath;
    }

    public void addCustomClassPath(URL customClassPath) {
        this.customClassPath.add(customClassPath);
    }

    public Collection<String> getClassPathDirs() {
        return classPathDirs;
    }

    public void addClassPathDir(String classPathDir) {
        this.classPathDirs.add(classPathDir);
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    public boolean isSslEnable() {
        return sslConfig.isEnable();
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    protected String processContextPath(String contextPath) {
        contextPath = contextPath.trim();

        if (contextPath.isEmpty()) {
            return "/";
        }

        // 添加前缀 "/"
        if (!contextPath.startsWith("/")) {
            contextPath = "/" + contextPath;
        }
        // 去除后缀 "/"
        if (contextPath.endsWith("/")) {
            contextPath = contextPath.substring(0, contextPath.length() - 1);
        }

        return contextPath;
    }

    protected void checkGzipLevel(int gzipLevel) {
        if (gzipLevel != -1 && (gzipLevel < 1 || gzipLevel > 9)) {
            throw new IllegalArgumentException("gzipLevel 可配置的值为: -1, 1, 2, 3, 4, 5, 6, 7, 8, 9");
        }
    }
}